package com.klikmee.sms.chat.web.service;

import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Observer;

import org.apache.log4j.Logger;

import com.klikmee.sms.cef.CoreAdapter;
import com.klikmee.sms.cef.services.Service;
import com.klikmee.sms.chat.web.dao.AnswerDAO;
import com.klikmee.sms.chat.web.dao.QuestionDAO;
import com.klikmee.sms.chat.web.filters.MessageFilter;
import com.klikmee.sms.services.chat.model.Answer;
import com.klikmee.sms.services.chat.model.BulkGroup;
import com.klikmee.sms.services.chat.model.BulkGroupItem;
import com.klikmee.sms.services.chat.model.Question;

public class SMSChatServiceImpl implements ChatService {

	/**
	 * Logger instance
	 */
	private Logger logger = Logger.getLogger(SMSChatServiceImpl.class);
	/**
	 * Sms core service id used to indentify this service during send msg
	 * requests
	 */
	private Long serviceId = null;
	/**
	 * Normal response charging id
	 */
	private Long responseChargingId = null;
	/**
	 * Info charging id, used to
	 */
	private Long infoChargingId = null;
	/**
	 * Bulk message charging id, used when sending charging messages
	 */
	private Long bulkChargingId = null;
	/**
	 * Dao instances used for database access
	 */
	private QuestionDAO questionDAO = null;
	private AnswerDAO answerDAO = null;
	/**
	 * Reference to core adapter, used to communicate with the core platform
	 */
	private CoreAdapter coreAdapter = null;

	private String serviceShortCode = null;
	private String autoResponseText = null;
	/**
	 * Constants used when processing delivery reports
	 */
	public static final String ANSWER_DELIVERED = "DELIVERED";
	public static final String ANSWER_NOT_DELIVERED = "NOT DELIVERED";

	/**
	 * Observable instance used to notify listeners when new request message is
	 * received.
	 */
	private SmsChatEventDispatcher onNewRequestEventDispatcher = new SmsChatEventDispatcher();

	/**
	 * Observable instance used to notify listener when response is sent back to
	 * the customer
	 */
	private SmsChatEventDispatcher onResponseSentEventDispatcher = new SmsChatEventDispatcher();

	/**
	 * List of observers for new request messages
	 */
	private List<Observer> newRequestListeners = null;
	/**
	 * List of observers for response message
	 */
	private List<Observer> responseSentListeners = null;

	/**
	 * List of request message filters
	 */
	private List<MessageFilter> messageFilters = null;

	/**
	 * This method initializes service. Subscribes listeners for new request and
	 * response events.
	 */
	public void init() {
		// register listeners for new request messages
		for (Observer observer : this.newRequestListeners) {
			onNewRequestEventDispatcher.addObserver(observer);
		}
		logger.info(String.format("Successfully subscribed %s listeners to the new request message event", newRequestListeners.size()));
		// register listeners for response messages
		for (Observer observer : this.responseSentListeners) {
			onResponseSentEventDispatcher.addObserver(observer);
		}
		logger.info(String.format("Successfully subscribed %s listeners to the response sent event", responseSentListeners.size()));

		logger.info("SMS Chat service initialized.");
	}

	/**
	 * This method is invoked by web service on arrival of a service request.
	 * Saves new request message to database, and notifies any listeners of this
	 * event.
	 * 
	 * @param coreId
	 *            coreId of the message, later used to send response messages
	 * @param timeReceived
	 *            request reception time
	 * @param fromNumber
	 *            identifier of the sender of the message
	 * @param toNumber
	 *            number request was sent to, usually service's short code
	 * @param messageText
	 *            text of the message
	 */
	@Override
	public void onRequestMessage(Long coreId, Date timeReceived, String fromNumber, String toNumber, String messageText) {

		// create question object and store it in db
		Question question = new Question();
		question.setCoreID(coreId);
		question.setDateReceived(timeReceived);
		question.setFromNumber(fromNumber);
		question.setQuestion(messageText);
		question.setToNumber(toNumber);
		question.setVisable(true);
		// create question
		questionDAO.create(question);

		logger.debug(String.format("Received new reqeust message coreId = [%s], from = [%s], text = [%s]", coreId, fromNumber, messageText));

		// notify listeners of the new request message received
		onNewRequestEventDispatcher.notifyListeners(question);
		
		// execute message listener chain
		for (MessageFilter messageFilter : messageFilters) {

			boolean isFilterActivated = messageFilter.processMessage(question);

			// break the iteration if one of the filters is activated
			if(isFilterActivated){
				break;
			}
		}
	}

	/**
	 * 
	 */

	@Override
	public void onResponseReport(Long responseId, String status) {

		// find answer by responseId
		Answer answer = answerDAO.findByResponseId(responseId);

		if (answer != null) {
			// resolve status
			if (status.equals(SMSChatServiceImpl.ANSWER_DELIVERED)) {
				answer.setAnswerDelivered(true);
			} else if (status.equals(SMSChatServiceImpl.ANSWER_NOT_DELIVERED)) {
				answer.setAnswerDelivered(false);
			}
			this.answerDAO.edit(answer);
		} else {
			logger.fatal(String.format("Received status report for unknowen answer, coreId = [%s]", responseId));
		}

		logger.debug(String.format("Processed status report for message coreId = [%s] with status [%s]", responseId, status));
	}

	/**
	 * Invoked when new subscription to service occurs.
	 */

	public void onSubscriptionApproved(String distributionList, String subscriberId, Date validTo) {
		// TODO Auto-generated method stub

	}

	@Override
	public void onSubscriptionExtension(String distributionListId, String msisdn, Date time) {
		// TODO Auto-generated method stub

	}

	@Override
	public void onRemoveSubscription(String distributionListId, String msisdn) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.klikmee.sms.chat.web.service.ChatService#sendResponseMessage(java
	 * .lang.Long, java.lang.String)
	 */
	@Override
	public Long sendResponseMessage(Long referenceId, String messageText) {
		// send response message
		Long coreId = this.coreAdapter.sendMessage(this.responseChargingId, this.serviceId, Service.SERVICE_FLAG_SEMANTIC_OK, referenceId, messageText);
		// notify listeners
		this.onResponseSentEventDispatcher.notifyListeners(coreId);

		logger.info(String.format("Response message with core id = %s sent to the customer", coreId));
		return coreId;
	}

	/**
	 * Sends bulk message to the group of receivers
	 * 
	 * @param message
	 *            text of message
	 * @param group
	 *            group to send message to
	 * @param signatureText
	 * @return
	 */
	public int sendBulkMessage(String message, BulkGroup group, String signatureText) {
		Collection<String> receivers = new LinkedList<String>();

		// itterate trough all items
		for (BulkGroupItem item : group.getBulkGroupItemSet()) {
			receivers.add(item.getPhoneNumber());
		}

		int numberOfSentMessages = this.coreAdapter.sendBulkMessage(this.bulkChargingId, this.serviceId, receivers, message, signatureText);
		return numberOfSentMessages;
	}

	/**
	 * Sens a bulk message to a single receiver
	 * 
	 * @param receiver
	 *            phone number of a receiver
	 * @param messageText
	 *            message text
	 * @return
	 */
	public int sendSingleBulk(String receiver, String messageText, String signatureText) {
		Collection<String> receivers = new LinkedList<String>();
		receivers.add(receiver);

		int numberOfSentMessages = this.coreAdapter.sendBulkMessage(this.bulkChargingId, this.serviceId, receivers, messageText, signatureText);
		return numberOfSentMessages;
	}

	public void setCoreAdapter(CoreAdapter coreAdapter) {
		this.coreAdapter = coreAdapter;
	}

	public void setServiceId(Long serviceId) {
		this.serviceId = serviceId;
	}

	public void setQuestionDAO(QuestionDAO questionDAO) {
		this.questionDAO = questionDAO;
	}

	public void setAnswerDAO(AnswerDAO answerDAO) {
		this.answerDAO = answerDAO;
	}

	public String getServiceShortCode() {
		return serviceShortCode;
	}

	public void setServiceShortCode(String serviceShortCode) {
		this.serviceShortCode = serviceShortCode;
	}

	public Long getServiceId() {
		return serviceId;
	}

	public QuestionDAO getQuestionDAO() {
		return questionDAO;
	}

	public AnswerDAO getAnswerDAO() {
		return answerDAO;
	}

	public CoreAdapter getCoreAdapter() {
		return coreAdapter;
	}

	public Long getBulkChargingId() {
		return bulkChargingId;
	}

	public void setBulkChargingId(Long bulkChargingId) {
		this.bulkChargingId = bulkChargingId;
	}

	public void setAutoResponseText(String autoResponseText) {
		this.autoResponseText = autoResponseText;
	}

	public String getAutoResponseText() {
		return autoResponseText;
	}

	public void setInfoChargingId(Long infoChargingId) {
		this.infoChargingId = infoChargingId;
	}

	public Long getInfoChargingId() {
		return infoChargingId;
	}

	public Long getResponseChargingId() {
		return responseChargingId;
	}

	public void setResponseChargingId(Long responseChargingId) {
		this.responseChargingId = responseChargingId;
	}

	public void setNewRequestListeners(List<Observer> newRequestListeners) {
		this.newRequestListeners = newRequestListeners;
	}

	public void setResponseSentListeners(List<Observer> responseSentListeners) {
		this.responseSentListeners = responseSentListeners;
	}
	
	public void setMessageFilters(List<MessageFilter> messageFilters) {
		this.messageFilters = messageFilters;
	}
}
